﻿#include "CDeviceManager.h"

#include <map>
#include <memory>

#include "CDeviceManagerConfig.h"
#include "logger.h"

class CDeviceManager::CDeviceManagerPrivate
{
public:
    std::map<std::string, std::shared_ptr<zl::zlbase::IDevice>> devices_;
    std::vector<ZLBasePlugin> plugins_;
    ZLDeviceConfig config_;

    CDeviceManagerPrivate() {}
    ~CDeviceManagerPrivate() {}
};

CDeviceManager &CDeviceManager::Instance()
{
    static CDeviceManager *_Instance = new CDeviceManager;
    return *_Instance;
}

CDeviceManager::CDeviceManager() :
    p_(new CDeviceManagerPrivate)
{
    system("mkdir config");
}

CDeviceManager::~CDeviceManager()
{
    delete p_;
}

bool CDeviceManager::LoadDeviceConfiguration(const std::string &path)
{
    // clear old configuration
    p_->config_ = ZLDeviceConfig();
    p_->devices_.clear();

    // read config file
    CDeviceManagerConfig config;
    auto ret = config.Read(p_->config_, path);
    if (!ret)
    {
        LOG(ERROR) << "read device configuration failed";
        return false;
    }
    LOG(INFO) << "read device configuration success";

    // create device
    LOG(INFO) << "create zlbase devices...";
    zl::zlbase::CDeviceFactory<zl::zlbase::IDoorControlBoard> factory_dcb;
    zl::zlbase::CDeviceFactory<zl::zlbase::IMonitorControlBoard> factory_mcb;
    zl::zlbase::CDeviceFactory<zl::zlbase::ITemperatureControlBoard> factory_tcb;
    for (const auto &d : p_->config_.zlbase_info.devices)
    {
        switch (d.type)
        {
            case zl::zlbase::EDeviceType_DCB:
                create_zlbase_device(d, factory_dcb);
                break;
            case zl::zlbase::EDeviceType_MCB:
                create_zlbase_device(d, factory_mcb);
                break;
            case zl::zlbase::EDeviceType_TCB:
                create_zlbase_device(d, factory_tcb);
                break;
            default:
                break;
        }
    }
    LOG(INFO) << "create zlbase device done, total zlbase devices: " << p_->devices_.size();

    return true;
    // todo create external device
}

bool CDeviceManager::SaveDeviceConfiguration(const std::string &path)
{
    CDeviceManagerConfig config;
    auto ret = config.Write(p_->config_, path);
    if (!ret)
    {
        LOG(ERROR) << "write device configuration failed";
        return false;
    }
    LOG(INFO) << "write device configuration success";

    return true;
}

void CDeviceManager::LoadZLBasePlugins()
{
    zl::zlbase::PluginInfo plugins[100];
    auto cnt = zl::zlbase::ZLApiInstance::GetPlugins(plugins, 100);
    LOG(INFO) << "load zlbase plugins: " << cnt << std::endl;
    for (size_t i = 0; i < cnt; ++i)
    {
        ZLBasePlugin plugin;
        plugin.type = plugins[i].type;
        plugin.name = plugins[i].name;
        plugin.version = plugins[i].version;
        plugin.uuid = plugins[i].uuid;
        plugin.filename = plugins[i].filename;
        p_->plugins_.push_back(plugin);
    }
}

std::shared_ptr<zl::zlbase::IDevice> CDeviceManager::GetDevice(const std::string &id)
{
    if (p_->devices_.find(id) == p_->devices_.cend())
    {
        return nullptr;
    }

    return p_->devices_[id];
}

std::shared_ptr<zl::zlbase::IDoorControlBoard>
CDeviceManager::GetDoorControlBoard(const std::string &id)
{
    auto idevice = GetDevice(id);
    auto device = std::dynamic_pointer_cast<zl::zlbase::IDoorControlBoard>(idevice);
    return device;
}

std::shared_ptr<zl::zlbase::IMonitorControlBoard>
CDeviceManager::GetMonitorControlBoard(const std::string &id)
{
    auto idevice = GetDevice(id);
    auto device = std::dynamic_pointer_cast<zl::zlbase::IMonitorControlBoard>(idevice);
    return device;
}

std::shared_ptr<zl::zlbase::ITemperatureControlBoard>
CDeviceManager::GetTemperatureControlBoard(const std::string &id)
{
    auto idevice = GetDevice(id);
    auto device = std::dynamic_pointer_cast<zl::zlbase::ITemperatureControlBoard>(idevice);
    return device;
}

ZLDeviceConfig CDeviceManager::GetDeviceConfig()
{
    return p_->config_;
}

void CDeviceManager::SetDeviceConfig(const ZLDeviceConfig &device_config)
{
    // todo device update
    p_->config_ = device_config;
    p_->devices_.clear();

    // create device
    LOG(INFO) << "create zlbase devices...";
    zl::zlbase::CDeviceFactory<zl::zlbase::IDoorControlBoard> factory_dcb;
    zl::zlbase::CDeviceFactory<zl::zlbase::IMonitorControlBoard> factory_mcb;
    zl::zlbase::CDeviceFactory<zl::zlbase::ITemperatureControlBoard> factory_tcb;
    for (const auto &d : p_->config_.zlbase_info.devices)
    {
        switch (d.type)
        {
            case zl::zlbase::EDeviceType_DCB:
                create_zlbase_device(d, factory_dcb);
                break;
            case zl::zlbase::EDeviceType_MCB:
                create_zlbase_device(d, factory_mcb);
                break;
            case zl::zlbase::EDeviceType_TCB:
                create_zlbase_device(d, factory_tcb);
                break;
            default:
                break;
        }
    }
    LOG(INFO) << "create zlbase device done, total zlbase devices: " << p_->devices_.size();

    // todo create external device
}

void CDeviceManager::GetDeviceInfo(const std::string id, ZLBaseDevice &device)
{
    for (const auto &d : p_->config_.zlbase_info.devices)
    {
        if (id == d.id)
        {
            device = d;
        }
    }
}

void CDeviceManager::GetDeviceInfo(const std::string id, ZLPeripheralsDevice &device)
{
    for (const auto &d : p_->config_.peripherals_info.devices)
    {
        if (id == d.id)
        {
            device = d;
        }
    }
}

void CDeviceManager::GetPlugins(std::vector<ZLBasePlugin> &plugins)
{
    plugins = p_->plugins_;
}

void CDeviceManager::GetPlugins(int32_t type, std::vector<ZLBasePlugin> &plugins)
{
    for (const auto &p : p_->plugins_)
    {
        if (p.type == type)
        {
            plugins.push_back(p);
        }
    }
}

bool CDeviceManager::GetPluginInfo(const std::string &uuid, ZLBasePlugin &plugin)
{
    for (const auto &p : p_->plugins_)
    {
        if (p.uuid == uuid)
        {
            plugin = p;
            return true;
        }
    }

    return false;
}

template <class Factory>
void CDeviceManager::create_zlbase_device(const ZLBaseDevice &d, Factory &factory)
{
    auto device = factory.Concreate(d.protocol.uuid.c_str(), d.datalink);
    if (device == nullptr)
    {
        LOG(ERROR) << "create device failed: " << d.name << "@" << d.id;
        return;
    }
    LOG(INFO) << "create device sucess: " << d.name << "@" << d.id;

    // set datalink callback
    auto datalink = device->GetDatalink();
    if (datalink != nullptr && d.enable_debug)
    {
        device->GetDatalink()->SetHandler([](int type, const char *origin, const unsigned char *buff, size_t buff_len) {
            if (buff == nullptr || buff_len <= 0)
            {
                return;
            }

            std::ostringstream oss;
            for (size_t i = 0; i < buff_len; ++i)
            {
                oss.width(2);
                oss.fill('0');
                oss << std::hex << int(buff[i]);
                oss << " ";
            }

            MSG(INFO) << "[" << origin << "]" << (type == 0 ? "[W] " : "[R] ") << oss.str();
        });
    }

    // set device property
    device->SetNumber(d.number);
    auto ret = device->Open();
    LOG(INFO) << "device open result: " << ret;
    MSG(INFO) << "[ID: " << d.id << "]"
              << "[" << d.name << " @ " << d.number << "] "
              << QObject::tr("open result: ").toStdString() << CRet(ret).string().toStdString();
    auto tmp = std::dynamic_pointer_cast<zl::zlbase::IDevice>(device);
    p_->devices_.insert(std::make_pair(d.id, tmp));
}
